﻿using System;
using System.Collections.Generic;

namespace Tiscs.Practices.Diagnostics
{
    /// <summary>
    /// 参数验证器。
    /// </summary>
    /// <typeparam name="T">要验证的参数的类型。</typeparam>
    public sealed class ParameterValidator<T>
    {
        /// <summary>
        /// 构造方法。
        /// </summary>
        /// <param name="name">要验证的参数的名称。</param>
        internal ParameterValidator(string name)
        {
            _parameterName = name;
        }

        /// <summary>
        /// 参数是否允许为空。
        /// </summary>
        private bool _allowNull = true;

        /// <summary>
        /// 要验证的参数的名称。
        /// </summary>
        private string _parameterName;

        /// <summary>
        /// 参数为空异常信息。
        /// </summary>
        private string _argumentNullException = "The parameter '{0}' must not be null.";

        /// <summary>
        /// 参数验证规则列表。
        /// </summary>
        private List<ParameterRule<T>> _rules = new List<ParameterRule<T>>();

        /// <summary>
        /// 获取要验证的参数的名称。
        /// </summary>
        public string ParameterName
        {
            get
            {
                return _parameterName;
            }
        }

        /// <summary>
        /// 设置要验证的参数的名称。
        /// </summary>
        /// <param name="name">要验证的参数的名称。</param>
        /// <returns>当前参数验证器实例。</returns>
        public ParameterValidator<T> SetParameterName(string name)
        {
            _parameterName = name;
            return this;
        }

        /// <summary>
        /// 验证指定的参数。如果不符合规则则返回验证异常信息，否则返回null。
        /// </summary>
        /// <param name="value">要验证的参数。</param>
        /// <returns>验证异常信息。</returns>
        public Exception Validate(T value)
        {
            if (!_allowNull && value == null)
            {
                return new ArgumentNullException(string.Format(_argumentNullException, _parameterName), _parameterName);
            }
            if (_allowNull && value == null)
            {
                return null;
            }
            foreach (ParameterRule<T> rule in _rules)
            {
                if (!rule.Predicate(value))
                {
                    return rule.ExceptionCreator(_parameterName);
                }
            }
            return null;
        }

        /// <summary>
        /// 检查指定的参数并设置验证异常信息。如果不符合规则则返回false，否则返回true。
        /// </summary>
        /// <param name="value">要验证的参数。</param>
        /// <param name="exception">验证异常信息。</param>
        /// <returns>参数检查结果。</returns>
        public bool CheckRules(T value, out Exception exception)
        {
            exception = Validate(value);
            return exception == null;
        }

        /// <summary>
        /// 检查指定的参数。如果不符合规则则返回false，否则返回true。
        /// </summary>
        /// <param name="value">要验证的参数。</param>
        /// <returns>参数检查结果。</returns>
        public bool CheckRules(T value)
        {
            if (!_allowNull && value == null)
            {
                return false;
            }
            if (_allowNull && value == null)
            {
                return true;
            }
            foreach (ParameterRule<T> rule in _rules)
            {
                if (!rule.Predicate(value))
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 参数断言验证。
        /// </summary>
        /// <param name="predicate">参数验证方法。</param>
        /// <param name="exceptionCreator">创建异常信息的方法。</param>
        /// <returns>当前参数验证器实例。</returns>
        public ParameterValidator<T> Assert(Func<T, bool> predicate, Func<string, Exception> exceptionCreator)
        {
            _rules.Add(new ParameterRule<T>(predicate, exceptionCreator));
            return this;
        }

        /// <summary>
        /// 参数为空验证。
        /// </summary>
        /// <returns>当前参数验证器实例。</returns>
        public ParameterValidator<T> AllowNull()
        {
            _allowNull = true;
            _argumentNullException = null;
            return this;
        }

        /// <summary>
        /// 参数不为空验证。
        /// </summary>
        /// <param name="exception">参数不为空异常信息。</param>
        /// <returns>当前参数验证器实例。</returns>
        public ParameterValidator<T> AssertNotNull(string exception = null)
        {
            _allowNull = false;
            if (exception != null)
            {
                _argumentNullException = exception;
            }
            return this;
        }
    }
}
